﻿/// <reference path="jquery-1.11.1.js" />

/*! jQuery.Net 1.0
 * 
 *
 * Copyright (c) 2010 Kenneth C. Carter
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 */

(function ($, undefined) {
    var json = {
        m: {
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"': '\\"',
            '\\': '\\\\'
        },
        s: {
            'array': function (x) {
                var a = ['['], b, f, i, l = x.length, v;
                for (i = 0; i < l; i += 1) {
                    v = x[i];
                    f = s[typeof v];
                    if (f) {
                        v = f(v);
                        if (typeof v == 'string') {
                            if (b) {
                                a[a.length] = ',';
                            }
                            a[a.length] = v;
                            b = true;
                        }
                    }
                }
                a[a.length] = ']';
                return a.join('');
            },
            'boolean': function (x) {
                return String(x);
            },
            'null': function (x) {
                return "null";
            },
            'number': function (x) {
                return isFinite(x) ? String(x) : 'null';
            },
            'object': function (x) {
                if (x) {
                    if (x instanceof Array) {
                        return s.array(x);
                    }
                    var a = ['{'], b, f, i, v;
                    for (i in x) {
                        v = x[i];
                        f = s[typeof v];
                        if (f) {
                            v = f(v);
                            if (typeof v == 'string') {
                                if (b) {
                                    a[a.length] = ',';
                                }
                                a.push(s.string(i), ':', v);
                                b = true;
                            }
                        }
                    }
                    a[a.length] = '}';
                    return a.join('');
                }
                return 'null';
            },
            'string': function (x) {
                if (/["\\\x00-\x1f]/.test(x)) {
                    x = x.replace(/([\x00-\x1f\\"])/g, function (a, b) {
                        var c = json.m[b];
                        if (c) {
                            return c;
                        }
                        c = b.charCodeAt();
                        return '\\u00' +
                            Math.floor(c / 16).toString(16) +
                            (c % 16).toString(16);
                    });
                }
                return '"' + x + '"';
            }
        }
    };

	$.net = $.net || {};

	$.extend($.net, {
		point: function (x, y) {	/// point class definition
			this.x = x;
			this.y = y;

			this.isEmpty = function () {
				return this.x === 0 && this.y === 0;
			}
		},
		string: {
		    format: function (params) {
		        var j = $.makeArray(params);
		        function _replace(i, c) {
		            return this[c];
		        };
		        return this.replace(/\{\d+\}/g, $.proxy(_replace, j));
		    },
		    toJSON: function (obj) {
		        var f = isNaN(obj) ? s[typeof obj] : json.s['number'];
		        if (f) return f(obj);
		    },
		    parseJSON: function (str, safe) {
		        if (safe === undefined) safe = $.parseJSON.safe;
		        if (safe && !/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.test(str))
		            return undefined;
		        return eval('(' + str + ')');
		    }
		},
		storage: {
			_local: null,
			_session: null,
			_isKeyValid: function(key){
				if (!key || (typeof key !== "string" && typeof key !== "number")) {
					return false;
				}
				return true;
			},
			_init: function() {
			    if (localStorage) { // local storage is supported
			        this._local = window.localStorage;
			    }
			    if (sessionStorage) {   // session storage is supported
			        this._session = window.sessionStorage;
			    } else {    // session storage not supported - emulating
			        this._session = {
			            data: {},
			            setItem: function (key, val) {
			                this.data[key] = val;
			            },
			            getItem: function (key) {
			                var val = null;
			                if (this.data.hasOwnProperty(key)) {
			                    val = this.data[key];
			                }
			                return val;
			            },
			            clear: function () {
			                $.each(this.data, function (i) {
			                    delete this.data[i];
			                });
			            }
			        }
			    }
			},
			set: function (key, val, session) {
				var $t = this, result = true;

				session = session === undefined ? false : session;

				try{
				    if($t._isKeyValid(key)){
				        throw new Error('key is an invalid type.');
				    }
				    if ($t._local && !session) {
				        $t._local.setItem(key, typeof (val) === 'string' ? val : $.toJSON(val));
				    } else {
				        $t._session.setItem(key, typeof (val) === 'string' ? val : $.toJSON(val));
				    }
				} catch (er) {
				    result = false;
				}

				return result;
			},
			get: function (key) {
			    var val = null, $t = this;
			    if ($t._local && $t._local.getItem(key)) {
			        val = $t._local.getItem(key);
			    } else if ($t._session.getItem(key)) {
			        val = $t._session.getItem(key);
			    }
			    try {
			        return $.parseJSON(val);
			    } catch (err) {
			        return val;
			    }
			},
			clear: function () {
			    var $t = this;

			    if ($t._local) {
			        $t._local.clear();
			    }
			    if ($t._session) {
			        $t._session.clear();
			    }
			},
			keys: function () {
				var index = [], $t = this;

				$.each($t._local, function (i) {
				    if ($t._local.hasOwnProperty(i)) {
						index.push(i);
					}
				});
				$.each($t._session, function (i) {
				    if ($t._session.hasOwnProperty(i)) {
				        index.push(i);
				    }
				});

				return index;
			}
		},
		callback: function (selector, args, callback, async, target) {
			var _postdata = '', ele = $(selector),
				form = ele.parents('form:first'),
				encode = function (val) {
					if (encodeURIComponent) {
						return encodeURIComponent(value);
					} else {
						return window.escape(value);
					}
				},
				add = function (name, val) {
					_postdata = [_postdata, [encode(name), encode(val)].join('=')].join('&');
				},
				filter = function (data, type) {
					if (data.charAt(0) == 's') {
						return data.substring(1);
					} else {
						var i = data.indexOf('|'), length = (i != -1 ? parseInt(data.substring(0, i)) : 0);

						if (!isNaN(length) && length > 0) {
							var validation = data.substring(i + 1, i + length + 1), ele = $('input[name=__EVENTVALIDATION]');

							if (validation !== '') {
								if (ele.length == 0) {
									ele = $('<input type="hidden" name="__EVENTVALIDATION" />');
									$('form[name=aspnetForm]').appendTo(ele);
								}
								ele.val(validation);
							}
						}

						return data.substring(i + length + 1);
					}
				},
				send = function (callback, async) {
					$.ajax({
						type: 'POST',
						processData: false,
						async: async !== undefined ? true : false,
						dataFilter: filter,
						success: callback,
						error: function (xhr, opt, err) {
							callback.call($(selector), { success: false, msg: err }, xhr);
						}
					});
				};
			// if the form is a parent serialize it if not serialize the selected element
			_postdata = form.length > 0 ? form.serialize() : ele.serialize();

			add('__CALLBACKID', target || ele.attr('name') || '__Page');

			if (typeof (args) === 'string') {           /// the args are already a string just add them
				add('__CALLBACKPARAM', args);
			} else if (typeof (args) === 'object') {    /// we need to json serialize the args
				add('__CALLBACKPARAM', $.toJSON(args));
			}

			send(callback, async);

			return $(selector);
		},
		render: function (tmpl, data) {
		    var fn = null, n, ele = this.element[0], $tmpl = $.net.templates;

		    if ($tmpl[tmpl]) {
		        /// Use a pre-defined template, if available
		        fn = $tmpl[tmpl];
		    } else if ((n = $(tmpl)).length > 0) {
		        /// We're pulling from a script node
		        fn = $.net.tmpl_engine($.trim($(n).html()));
		    }

		    fn = fn || $.net.tmpl_engine(tmpl);

		    if ($.isArray(data)) {
		        return $.map(data, function (data, i) {
		            return fn.call(data, $, data, i);
		        });
		    } else {
		        return fn.call(data, $, data, 0);
		    }
		},
		templates: {},
		tmpl_cmd: {
		    each: {
		        _default: [null, "$2"],
		        prefix: "$.each($2,function(i, item){with(this){",
		        suffix: "}});"
		    },
		    "if": {
		        prefix: "if($2){",
		        suffix: "}"
		    },
		    "else": {
		        prefix: "}else{"
		    },
		    html: {
		        prefix: "_.push(typeof $2 === 'function' ? $2.call(this) : $2);"
		    },
		    "=": {
		        _default: ["this"],
		        prefix: "_.push($.net.encode(typeof $2 === 'function' ? $2.call(this) : $2));"
		    }
		},
		encode: function (text) {
		    return text !== null ? document.createTextNode(text.toString()).nodeValue : "";
		},
		tmpl_engine: function (str, data, i) {
		    var err = "", fn = null, func = "", tmpl_cmd = $.net.tmpl_cmd;
		    try {
		        fn = $.storage('get', str);
		        if (!fn) {
		            func = "var $=jQuery,_=[],index=$i,print=function(){_.push.apply(_, arguments);};with($data){" +
                        str.replace(/({%.+?%})/g, "%]$1[%")
                           .replace(/^(.+?)$/g, "[%$1%]")
                           .replace(/\[%[\s\t]?(.+?)%\]/g, '_.push("$1");')
                           .replace(/{%(\W|\w+)[\s]?[\(]?(.+?)?[\)]?[\s]?%}/gi, function (match, type, js) {
                               var args = arguments, index = args[args.length - 2], src = args[args.length - 1];

                               js = $.trim(js);

                               if (tmpl_cmd[type]) {
                                   if (js !== "") {
                                       return tmpl_cmd[type].prefix.replace(/\$2/g, js);
                                   } else {
                                       return tmpl_cmd[type].suffix;
                                   }
                               } else if (js !== "") {
                                   return js;
                               }
                           }) + "}return _.join('');"; //alert(func);
		            fn = new Function("jQuery", "$data", "$i", func);
		            $.storage('set', str, fn, true);
		        }
		        return data ? fn.call(this, JQuery, data, i) : fn;
		    } catch (e) { err = e.message; }
		    return "< # ERROR: " + $.net.encode(err) + " # >";
		}

	});
	// expose specific methods on the jquery object for ease of access.
	$.extend($, {
	    callback: $.net.callback,
	    storage: function (func, key, value, session) {
	        if (func !== undefined && func !== null && $.isFunction($.net.storage[func]) && arguments.length > 1 && !func.match(/^_/)) {
	            return $.net.storage[func].apply(this, $.makeArray(arguments).slice(1));
	        }

	        return this;
	    },
	    toJSON: $.net.string.toJSON,
	    parseJSON: $.extend($.net.string.parseJSON, { safe: false })
	});
	$.extend($.fn, {
	    widget: function (name, opt) {
	        var parts = String(name).split('.'), namespace = parts[0], ctrl = parts[1], _widget = $[namespace][ctrl];
	        // when calling into a name space in order to set the /*this*/ variable we have to instantiate a new instance of the widget.
	        if ($.isFunction(_widget)) {
	            _widget.call(new _widget(), opt, this);
	        }

	        return this;
	    },
	    render: function (tmpl, data, cmd) {
	        var args = arguments, $t = $(this);

	        if ($(tmpl).length === 0) {
	            // the template is not loaded we need to retrieve from server.
	            $.callback($t.selector, { event: 'template', args: { id: tmpl } },
                    function (_tmpl) {
                        if (!_tmpl.canceled && $(tmpl).length === 0) {
                            /// append the template to the head element
                            $(_tmpl.data).appendTo($('head:first'));
                        }
                    }, false);
	        }
	        if (cmd) {
	            $.extend($.net.tmpl_cmd, cmd);
	        }
	        if (args.length === 2 && typeof args[0] === "string" && typeof args[1] === "object") {
	            if (data.event === "autocomplete") {
	                $t.data(data.event)._renderItem = function (ele, item) {
	                    return $($.net.render.call(this, tmpl, item)).data(data.item, item).appendTo(ele);
	                };
	            }
	            return $t;
	        }
	        return $t.map(function (i, tmpl) {
	            return $.net.render(tmpl, data);
	        });
	    }
	});
    // initialize/extend data structures
	$.extend(String.prototype, {
        format: $.net.string.format
	});
	$.extend(String, {
	    format: function (str, params) {
	        return str.format($.makeArray(arguments).slice(1));
	    }
	});
	$.net.storage._init();
}(jQuery));